refactor: replace asyncapi-validator with custom AsyncApiValidator#110
refactor: replace asyncapi-validator with custom AsyncApiValidator#110
Conversation
Removes asyncapi-validator and its dependency chain (~160 packages including minimatch@3.1.2, CVE-2026-27904) in favor of a lightweight module that parses AsyncAPI 3.0 YAML with js-yaml and compiles AJV validators directly. Fixes sendError schema/code mismatch where data was sent as an object but the spec declared it as a string. Adds unit tests for the new module and a CI workflow to validate AsyncAPI specs on changes to api/source/specification/. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| name: Validate AsyncAPI specs | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: lts/* | ||
| - name: Validate log-socket.yaml | ||
| run: npx --yes @asyncapi/cli@2 validate api/source/specification/log-socket.yaml |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 months ago
In general, the fix is to add an explicit permissions block limiting the GITHUB_TOKEN to the least privileges needed. This workflow only checks out contents and runs a validation command; it does not write to the repo, issues, or pull requests. Therefore, contents: read (and nothing else) is sufficient.
The best minimal change without altering existing behavior is to add a permissions section at the workflow root (top level, alongside name and on). This will apply to all jobs, including validate_asyncapi, and avoids repeating the block per job. Concretely, in .github/workflows/api-spec-validation.yml, insert:
permissions:
contents: readbetween the existing on: block (ending at current line 15) and the jobs: key (current line 17). No additional imports or methods are needed; this is pure YAML configuration for GitHub Actions.
| @@ -14,6 +14,9 @@ | ||
| - "api/source/specification/**" | ||
| - ".github/workflows/api-spec-validation.yml" | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| validate_asyncapi: | ||
| name: Validate AsyncAPI specs |
…n 1.6.2 and install API dependencies for unit tests
…github workflow to run it.
| name: Run unit tests | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: lts/* | ||
| - name: Install API dependencies | ||
| run: npm ci | ||
| working-directory: ./api/source/ | ||
| - name: Install test dependencies | ||
| run: npm ci | ||
| working-directory: ./test/unit/ | ||
| - name: Run unit tests | ||
| working-directory: ./test/unit/ | ||
| run: npm test |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 months ago
In general, the fix is to explicitly declare a permissions block that restricts the GITHUB_TOKEN to the least privilege required. For this unit test workflow, all steps only read repository contents and do not perform any write operations to GitHub resources, so contents: read at the workflow level is sufficient.
The best way to fix this without changing functionality is to add a root-level permissions section just under the name: line (before on:). This will apply to all jobs in the workflow, including unit-tests, unless overridden by a job-specific permissions block. No changes to the steps are required. The new block should be:
permissions:
contents: readThis is sufficient because actions/checkout, actions/setup-node, and npm commands used locally do not require write access to the GitHub API. No imports or additional definitions are needed; only this YAML change in .github/workflows/unit-tests.yml.
| @@ -1,4 +1,6 @@ | ||
| name: Unit tests | ||
| permissions: | ||
| contents: read | ||
| on: | ||
| workflow_dispatch: | ||
| pull_request: |
|
|
||
| function tempSpec(content) { | ||
| const tmpPath = path.join(os.tmpdir(), `asyncapi-test-${Date.now()}-${Math.random().toString(36).slice(2)}.yaml`) | ||
| fs.writeFileSync(tmpPath, content, 'utf8') |
Check failure
Code scanning / CodeQL
Insecure temporary file High test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 months ago
In general, to fix insecure temporary file creation, avoid manually concatenating os.tmpdir() with a generated name and then writing to it. Instead, delegate to a library or API that creates the file atomically with secure permissions and returns an already-open file or its unique path. In Node.js, a common solution is the tmp package (or fs.mkdtemp for directories, combined with controlled file creation).
For this specific file, the best fix is to replace the tempSpec implementation so that it uses tmp.fileSync to securely create the temporary file. tmp.fileSync will create a unique file in the system temp directory with safe permissions, and return an object including the file’s name and a file descriptor. We then write the content to name instead of constructing the path ourselves. Concretely:
- Add
import tmp from 'tmp'near the other imports at the top oftest/unit/mocha/asyncApiValidator.test.js. - Change
tempSpecso that:- It calls
tmp.fileSync({ postfix: '.yaml' })to create the temp file. - It writes
contenttofile.name(the secure, already-created file path). - It pushes
file.nameintotmpFilesfor later cleanup. - It returns
file.name.
- It calls
No other behavior of the tests changes: they still get a path to a YAML file on disk containing the given content, and the afterEach cleanup still unlinks those files.
| @@ -3,6 +3,7 @@ | ||
| import path from 'node:path' | ||
| import fs from 'node:fs' | ||
| import os from 'node:os' | ||
| import tmp from 'tmp' | ||
|
|
||
| const __dirname = path.dirname(fileURLToPath(import.meta.url)) | ||
| const specPath = path.resolve(__dirname, '../../../api/source/specification/log-socket.yaml') | ||
| @@ -61,10 +62,10 @@ | ||
| }) | ||
|
|
||
| function tempSpec(content) { | ||
| const tmpPath = path.join(os.tmpdir(), `asyncapi-test-${Date.now()}-${Math.random().toString(36).slice(2)}.yaml`) | ||
| fs.writeFileSync(tmpPath, content, 'utf8') | ||
| tmpFiles.push(tmpPath) | ||
| return tmpPath | ||
| const tmpFile = tmp.fileSync({ postfix: '.yaml' }) | ||
| fs.writeFileSync(tmpFile.name, content, 'utf8') | ||
| tmpFiles.push(tmpFile.name) | ||
| return tmpFile.name | ||
| } | ||
|
|
||
| describe('fromSource - real spec', function () { |
| @@ -6,6 +6,7 @@ | ||
| "dependencies": { | ||
| "chai": "^5.2.0", | ||
| "mocha": "^11.0.1", | ||
| "mochawesome": "^7.1.3" | ||
| "mochawesome": "^7.1.3", | ||
| "tmp": "^0.2.5" | ||
| } | ||
| } |
| Package | Version | Security advisories |
| tmp (npm) | 0.2.5 | None |
Removes asyncapi-validator and its dependency chain (~160 packages including minimatch@3.1.2, CVE-2026-27904) in favor of a lightweight module that parses AsyncAPI 3.0 YAML with js-yaml and compiles AJV validators directly. Fixes sendError schema/code mismatch where data was sent as an object but the spec declared it as a string.
Adds unit tests for the new module and a CI workflow to validate AsyncAPI specs on changes to api/source/specification/.